/**
 * 2017年7月20日
 */
package cn.edu.bjtu.workbench;

import org.deeplearning4j.eval.Evaluation;
import org.deeplearning4j.iterator.LabeledSentenceProvider;
import org.deeplearning4j.iterator.provider.CollectionLabeledSentenceProvider;
import org.deeplearning4j.models.embeddings.loader.WordVectorSerializer;
import org.deeplearning4j.models.word2vec.Word2Vec;
import org.deeplearning4j.nn.conf.ComputationGraphConfiguration;
import org.deeplearning4j.nn.conf.ConvolutionMode;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.Updater;
import org.deeplearning4j.nn.conf.graph.MergeVertex;
import org.deeplearning4j.nn.conf.layers.ConvolutionLayer;
import org.deeplearning4j.nn.conf.layers.GlobalPoolingLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.conf.layers.PoolingType;
import org.deeplearning4j.nn.graph.ComputationGraph;
import org.deeplearning4j.nn.weights.WeightInit;
import org.deeplearning4j.text.tokenization.tokenizerfactory.TokenizerFactory;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.lossfunctions.LossFunctions;

import cn.edu.bjtu.workbench.datasource.lsp.FDLabeledSentenceExceptionSupportProvider;
import cn.edu.bjtu.workbench.tokenization.AnsjTokenzierFactory;
import lombok.NonNull;

import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.dataset.DataSet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
 * @author Alex
 *
 */
public class Temp {
	public static void main(String[] args) throws IOException, InterruptedException {
		//咱们就开始构造一个datasetiterator 
		//怎么构造呢   很简单,它需要什么参数咱们给它什么参数就可以了
		
		//先读入word2vec
		//org.deeplearning4j.models.word2vec.Word2Vec@43df23d3 w2v
		Word2Vec w2v = WordVectorSerializer.readWord2VecModel("D:\\textdata\\deep4jdata\\mymodel\\fdModel_100.txt");
		//还需要一个分词式  我这里就使用Ansj
		//这里解释下什么叫耦合
		//上面的import中  引用了一些东西 有的是接口 有的是具体类  
		//我们推荐的做法是只引入接口而不引入类,引入类了之后  就耦合到一起了
		
		TokenizerFactory tf = new AnsjTokenzierFactory();
		
		//下面我们要读取数据集了
		//先弄一个LSP     LSP就是labeledSentenceProvider
		//我这里就先用以前写的一个类叫FDLabeledSentenceExceptionSupportProvider
		String path = "D:\\textdata\\deep4jdata\\mymodel\\fddata_train";
//		LabeledSentenceProvider lsp  = new FDLabeledSentenceExceptionSupportProvider(path);
		//写完一句就测试下
		//一般hasNxt 组合next方法都要求先调用一个hasNext 进行数据的初始化 等操作.
//		if(lsp.hasNext()){
//			Pair<String, String> sen = lsp.nextSentence();
//			System.out.println(sen.getFirst());
//			System.out.println(sen.getSecond());
//		}
		
		
	//	System.out.println(w2v);
		//这样就得到了DSI
		
		
		
		
		
//		DataSetIterator ds = new CnnSentenceDataSetIterator.Builder()
//				.sentenceProvider(lsp)
//				.wordVectors(w2v)
//				//忘了设置一个参数 最长句子长度
//				.maxSentenceLength(256)
//				.minibatchSize(32)
//				.tokenizerFactory(tf)
//				.build();
//		
//		//全0数据也是有问题的.
//		if(ds.hasNext()){
//			DataSet x = ds.next();
//			System.out.println(x);
//		}
		//这样就把一个数据集完成了预处理任务   然后形成了一个batch的矩阵   
	
		
		
		//现在是直接读取的原来一个文件中的数据集,这个数据集是比较长,量比较大,不方便调试程序的,所以,如果想调试自己的程序 就想选择比较短点的句子,方便自己看变量
		//的状态及中间结果是不是合理,所以我们要换一批测试数据,这种情况下  我们就要换一个"数据源"  
		//原来的数据源是 FDLabeledSentenceExceptionSupportProvider 现在我们自己定义一个句子源
		//所以,我们就根据两个List构造自己的数据源
		//这个类就是CollectionLabeledSentenceProvider 
		//这个玩意需要两个List
		List<String> sentences = new ArrayList<>(2);
		List<String> labels = new ArrayList<String>(2);
		sentences.add("曹操是中国历史上一个著名的人物");
		sentences.add("我是中国人,我爱自己的祖国");
		labels.add("History");
		labels.add("Education");
		//这里注意除了LSP  像word2vec这种东西都改变的,用的同一份词向量
//		  public CollectionLabeledSentenceProvider(@NonNull List<String> sentences,
//                  @NonNull List<String> labelsForSentences) {
//      this(sentences, labelsForSentences, new Random());
//  }
//
//  public CollectionLabeledSentenceProvider(@NonNull List<String> sentences, @NonNull List<String> labelsForSentences,
//                  Random rng) {
		//这里我们要用后面那个构造函数.
		LabeledSentenceProvider clsp =  new CollectionLabeledSentenceProvider(sentences, labels,null);
		//看  这就是面向接口编程,当一个类具体实现改变之后了  如果你的方法是接受接口的,那么其他任何地方都是不受影响的.
		DataSetIterator ds = new CnnSentenceDataSetIterator.Builder()
				.sentenceProvider(clsp)
				.wordVectors(w2v)
				//忘了设置一个参数 最长句子长度
				.maxSentenceLength(5)
				.minibatchSize(32)
				.tokenizerFactory(tf)
				.build();
		
		//全0数据也是有问题的.
//		if(ds.hasNext()){
//			DataSet x = ds.next();
//			System.out.println(x);
//		}
		ComputationGraphConfiguration cgc = get();
		//到这里我就有一个神经网络了
		ComputationGraph cg = new ComputationGraph(cgc);
		//1 初始化它
		cg.init();
		//2训练它
		//就这些东西.
		//如果我要迭代100次
		int epoch = 100;
		for (int i = 0 ;i<epoch ;i ++){
			cg.fit(ds);
		}
		
		Evaluation e = cg.evaluate(ds);
		
		
		
		System.out.println(e.accuracy());
		System.out.println(e.f1());
		
		
	}
	static ComputationGraphConfiguration get(){
		int vectorSize = 100;
		int cnnLayerFeatureMaps = 100;
		  ComputationGraphConfiguration config = new NeuralNetConfiguration.Builder()
		            .weightInit(WeightInit.RELU)
		            .activation(Activation.LEAKYRELU)
		            .updater(Updater.ADAM)
		            .convolutionMode(ConvolutionMode.Same)      //This is important so we can 'stack' the results later
		            .regularization(true).l2(0.0001)
		            .learningRate(0.01)
		            .graphBuilder()
		            .addInputs("input")
		            .addLayer("cnn3", new ConvolutionLayer.Builder()
		                .kernelSize(3,vectorSize)
		                .stride(1,vectorSize)
		                .nIn(1)
		                .nOut(cnnLayerFeatureMaps)
		                .build(), "input")
		            .addLayer("cnn4", new ConvolutionLayer.Builder()
		                .kernelSize(4,vectorSize)
		                .stride(1,vectorSize)
		                .nIn(1)
		                .nOut(cnnLayerFeatureMaps)
		                .build(), "input")
		            .addLayer("cnn5", new ConvolutionLayer.Builder()
		                .kernelSize(5,vectorSize)
		                .stride(1,vectorSize)
		                .nIn(1)
		                .nOut(cnnLayerFeatureMaps)
		                .build(), "input")
		            .addVertex("merge", new MergeVertex(), "cnn3", "cnn4", "cnn5")      //Perform depth concatenation
		            .addLayer("globalPool", new GlobalPoolingLayer.Builder()
		                .poolingType(PoolingType.MAX)
		                .build(), "merge")
		            .addLayer("out", new OutputLayer.Builder()
		                .lossFunction(LossFunctions.LossFunction.MCXENT)
		                .activation(Activation.SOFTMAX)
		                .nIn(3*cnnLayerFeatureMaps)
		                .nOut(2)    //2 classes: positive or negative
		                .build(), "globalPool")
		            .setOutputs("out")
		            .build();
		  return config;
	}
}
